#include <basic/errcode.h>
#include <basic/datetime.h>
#include <basic/ossl.h>
#include <basic/dbc.h>
#include <basic/mysqlc.h>

#include <mysql.h>

#define _EXPAND_ROWS        20

struct _mysqlc
{/*{{{*/
    MYSQL mysql, *sock;
};/*}}}*/
static struct _mysqlc defdb;

int mysqlc_init()
{/*{{{*/
    my_init();
    return(0);
}/*}}}*/

int mysqlc_clean()
{/*{{{*/
    ossl_clean_mt();
    return(0);
}/*}}}*/

int mysqlc_connect_ex(char *dbuser, char *dbpasswd, char *database,
        char *host, char *charset, MYSQLC *pdb)
{/*{{{*/
    MYSQLC db;
    int len;

    if(dbuser == NULL ||
            (len=strlen(dbuser)) <= 0 || len > NAME_SIZE ||
            dbpasswd == NULL ||
            (len=strlen(dbpasswd)) < PASSWD_MIN_SIZE ||
            len > PASSWD_MAX_SIZE ||
            database == NULL ||
            (len=strlen(database)) <= 0 || len > NAME_SIZE ||
            (host && strlen(host) > NAME_SIZE))
        return(RET_ERR_PARA);
    if(host && BUF_IS_CLR(host))
        host=NULL;
    if(!charset || BUF_IS_CLR(charset))
        charset="utf8";

    if(pdb)
    {
        *pdb=NULL;
        ALLOC_RETERR(db, struct _mysqlc, MY_ERROR);
    }
    else
        db=&defdb;
    mysql_init(&db->mysql);
    if((db->sock = mysql_real_connect(
                    &db->mysql, host,
                    dbuser, dbpasswd, database,
                    0, NULL, CLIENT_FOUND_ROWS)) == NULL)
    {
        if(pdb) free(db);
        return(MY_ERROR);
    }
    if(mysql_set_character_set(&db->mysql, charset))
    {
        if(pdb) free(db);
        return(MY_ERROR);
    }
    //printf("%s\n", mysql_character_set_name(db));
    if(pdb) *pdb=db;

    return(MY_OK);
}/*}}}*/

int mysqlc_disconnect_ex(MYSQLC db)
{/*{{{*/
    if(!db) db=&defdb; 
    mysql_close(db->sock);
    db->sock=NULL;
    return(MY_OK);
}/*}}}*/

int mysqlc_begin_ex(MYSQLC db)
{/*{{{*/
    if(!db) db=&defdb; 
    if(mysql_query(db->sock, "begin"))
        return(mysql_errno(db->sock));
    return(MY_OK);
}/*}}}*/

int mysqlc_commit_ex(MYSQLC db)
{/*{{{*/
    if(!db) db=&defdb; 
    if(mysql_query(db->sock, "commit"))
        return(mysql_errno(db->sock));
    return(MY_OK);
}/*}}}*/

int mysqlc_rollback_ex(MYSQLC db)
{/*{{{*/
    if(!db) db=&defdb; 
    if(mysql_query(db->sock, "rollback"))
        return(mysql_errno(db->sock));
    return(MY_OK);
}/*}}}*/

int mysqlc_exec_ex(MYSQLC db, char *sql)
{/*{{{*/
    if(sql == NULL)
        return(MY_ERROR);
    if(!db) db=&defdb; 
    if(mysql_query(db->sock, sql))
        return(mysql_errno(db->sock));
    if(mysql_affected_rows(db->sock) == 0)
        return(MY_NO_DATA_FOUND);
    return(MY_OK);
}/*}}}*/

int mysqlc_query_ex(MYSQLC db, char *sql, int request_row_num,
    DBC_RSET *rset)
{/*{{{*/
    MYSQL_RES *res;
    MYSQL_ROW row;
    int row_idx, col_idx;
    int sqlcode=MY_ERROR;

    if(sql == NULL || rset == NULL)
        return(MY_ERROR);
    if(!db) db=&defdb; 

    // 初始化查询结果集
    rset->rows=NULL;
    rset->col_num=0;
    rset->row_num=0;
    rset->row_alloc_num=0;
    rset->query_flag=-1;

    // 查询
    if(mysql_query(db->sock, sql) ||
        (res=mysql_use_result(db->sock)) == NULL)
        return(mysql_errno(db->sock));
    rset->col_num=mysql_field_count(db->sock);

    // 查询结果集分配空间
    if(request_row_num > 0)
    {
        ALLOC_ARRAY(rset->rows, char **, request_row_num,
                rset->row_alloc_num, ERROR_SELECT);
        for(row_idx=0; row_idx<request_row_num; ++row_idx)
            rset->rows[row_idx]=NULL;
    }

    while(1)
    {
        if((row=mysql_fetch_row(res)) == NULL)
        {
            sqlcode=mysql_errno(db->sock);
            if(sqlcode)  goto ERROR_SELECT;
            break;
        }
        rset->row_num++;
        if(request_row_num > 0 &&
                rset->row_num == request_row_num+1)
            break;

        // 扩展行
        if(rset->row_num > rset->row_alloc_num)
        {
            EXPAND_ARRAY(rset->rows, char **, _EXPAND_ROWS,
                    rset->row_alloc_num, ERROR_SELECT);
            for(row_idx=rset->row_num-1; row_idx<_EXPAND_ROWS;
                row_idx++)
                rset->rows[row_idx]=NULL;
        }
        // 行
        row_idx=rset->row_num-1;
        ALLOC_MULTI(rset->rows[row_idx], char *, rset->col_num,
                ERROR_SELECT);
        for(col_idx=0; col_idx<rset->col_num; ++col_idx)
        {
            // 列
            if(row[col_idx])
            {
                ALLOC_MULTI(rset->rows[row_idx][col_idx], char,
                        strlen(row[col_idx])+1,
                        ERROR_SELECT);
                strcpy(rset->rows[row_idx][col_idx], row[col_idx]);
            }
            else
            {
                ALLOC_MULTI(rset->rows[row_idx][col_idx], char,
                        2, ERROR_SELECT);
                strcpy(rset->rows[row_idx][col_idx], SPACES);
            }
        }
    }

    /*----- 释放mysql结果集 -----*/
    mysql_free_result(res);

    /*----- 无记录 -----*/
    if(rset->row_num == 0)
    {
    	dbc_query_free(rset);
		return(MY_NO_DATA_FOUND);
    }
    /*----- 查询位置 -----*/
    if(request_row_num > 0 &&
        rset->row_num == request_row_num+1)
    {
        rset->query_flag=QUERY_HALFWAY;
        rset->row_num=request_row_num;
    }
    else
        rset->query_flag=QUERY_BOTTOM;

    return(MY_OK);

ERROR_SELECT:
    mysql_free_result(res);
    dbc_query_free(rset);
    return(sqlcode);
}/*}}}*/

int mysqlc_sequence_nextval_ex(MYSQLC db, char *seq, long *nextval)
{/*{{{*/
    char sql[200];

    if(seq == NULL || nextval == NULL)
        return(MY_ERROR);
    if(!db) db=&defdb; 

    sprintf(sql, "update %s set id=last_insert_id(id+1)", seq);
    if(mysql_query(db->sock, sql))
        return(mysql_errno(db->sock));
    if(mysql_affected_rows(db->sock) == 0)
        return(MY_NO_DATA_FOUND);
    *nextval=mysql_insert_id(db->sock);

    return(MY_OK);
}/*}}}*/
